﻿using ComLib.ComType;
using ComUIControl;
using DYPSensor;
using LogLib;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace IPCPages
{
    public delegate void MeasuringUpdateDelegate(int result);
    public delegate void MeasuringErrorReceivedDelegate(string error);
    public class Measuring
    {
        /// <summary>
        /// 错误信息
        /// </summary>
        private int _outlierCount = 0;
        public int OutlierCount
        {
            get { return _outlierCount; }
        }

        /// <summary>
        /// 错误信息
        /// </summary>
        private string _error = "";
        public string Error
        {
            get { return _error; }
        }

        public event MeasuringUpdateDelegate MeasuringUpdated;
        private void OnMeasuringUpdated(int result)
        {
            if (MeasuringUpdated != null)
            {
                MeasuringUpdated(result);
            }
        }

        /// <summary>
        /// 报告测量结果不正常的情况
        /// </summary>
        public event MeasuringUpdateDelegate MeasuringAbnormal;
        private void OnMeasuringAbnormal(int result)
        {
            if (MeasuringAbnormal != null)
            {
                MeasuringAbnormal(result);
            }
        }

        public event LadarErrorReceivedEventHandler ErrorReceived;
        private void OnErrorReceived(string error, int code)
        {
            if (ErrorReceived != null)
            {
                ErrorReceived(error, code);
            }
        }

        private DYPSensorService _sensor = new DYPSensorService();

        private AimingOption _option = new AimingOption();

        private IntRangle measuringRangle = new IntRangle();

        private IntRangle MostConfidentRangle = new IntRangle();
  
        /// <summary>
        /// 指示当前回合的位置
        /// </summary>
        private int _currentRoundsCount = 0;

        /// <summary>
        /// 指示一个回合中需要测量的次数
        /// </summary>
        private const int TimesOfOneRounds = 3;

        private const int MaxRoundsCount = 2;

        /// <summary>
        /// 回合测量结果
        /// </summary>
        private List<int> MeasuringResults = new List<int>();

        private void LoadAimingOption()
        {
            if (!File.Exists(_option.ConfigFilePath))
            {
                _option.Save();
            }
            _option.Load();
        }
       
        private void InitData()
        {
            _currentRoundsCount = 0;
            MeasuringResults.Clear();
        }

        public Measuring()
        {
            LoadAimingOption();
            MostConfidentRangle = _option.GetMostConfidentMeasuringRange();
            measuringRangle = _option.GetMaxMeasuringRange();
        }

        public bool OpenCom()
        {
            _sensor.ReadType = SensorReadType.DistanceFilterValue;
            _sensor.Open();
            if (_sensor.IsOpen)
            {
                _sensor.dataReceived += OnDataReceived;
                _sensor.errorReceived += OnErrorReceived;
                return true;
            }
            else
            {
                MsgBox.Show("串口打开失败:" + _sensor.Error);
                return false;
            }
        }

        public void CloseCom()
        {
            _sensor.dataReceived -= OnDataReceived;
            _sensor.errorReceived -= OnErrorReceived;
            _sensor.Close();
        }

        /// <summary>
        /// 启动测量服务
        /// </summary>
        public void StartWork()
        {
            _outlierCount = 0;
            Log.Write("开始测量高度...");
            InitData();
            _sensor.StartWorking();
        }

        /// <summary>
        /// 中止测量服务
        /// </summary>
        public void StopWork()
        {
            Log.Write("停止测量高度...");
            InitData();
            _sensor.StopWorking();
        }

        /// <summary>
        /// 使等待事件获得信号, 恢复被暂停/阻塞的工作项任务
        /// </summary>
        public void SetEvent()
        {
            _sensor.SetEvent();
        }

        /// <summary>
        /// 判断串口是否打开
        /// </summary>
        public bool IsOpen
        {
            get
            {
                if (_sensor != null)
                {
                    return _sensor.IsOpen;
                }
                return false;
            }
        }  

        private int GetAverageValue(List<int> array)
        {
            int sum = 0;
            int count = 0;
            foreach (int v in array)
            {
                sum += v;
                count++;
            }
            if (count > 0)
            {
                return sum / count;
            }
            return 0;
        }

        private void OnDataReceived(SensorReadResult result)
        {
            if(result.ReadType == SensorReadType.Distance
                || result.ReadType == SensorReadType.DistanceFilterValue
                 || result.ReadType == SensorReadType.All)
            {
                SaveResult((int)(result.ReadValue + 0.5));
                CheckMeasuringResults();
            }           
        }

        /// <summary>
        /// 计算真实的测量值
        /// </summary>
        /// <returns></returns>
        private int GetRealMeasuringResultFromRounds()
        {
            int value = 0;
            List<int> listResults = new List<int>();
            StringBuilder builder = new StringBuilder();
            builder.Append("本次回合的测量结果是:");

            // 试图找出最可能正确的值
            for (int i = 0; i < MeasuringResults.Count; i++)
            {
                builder.Append(MeasuringResults[i].ToString() + ", ");
                if (MostConfidentRangle.InRangle(MeasuringResults[i]))
                {
                    listResults.Add(MeasuringResults[i]);
                }
            }
            Log.Write(builder.ToString());
            if (listResults.Count >= 2)
            {
                listResults.Sort();
                if(listResults[1] - listResults[0] > 2)
                {
                    value = listResults[0];
                }
                else
                {
                    value = (listResults[0] + listResults[1]) / 2;
                }
                string logContent = string.Format("检测到有{0}次的身高值分布于最可能的区间内, 计算得到最小两次的平均值为{1}.", listResults, value);
                Log.Write(logContent);
                return value;
            }
            return GetUnlikelyMeasuringResult();
        }

        /// <summary>
        /// 获取非一般情况下的测量结果，例如实际身高偏矮的(<1m)
        /// </summary>
        private int GetUnlikelyMeasuringResult()
        {
            List<int> listResults = new List<int>();
            MeasuringResults.Sort();
            int min = MeasuringResults.First();
            int max = MeasuringResults.Last();
            int difference = max - min;
            //for(int i = 1;i<)
            //listResults = MeasuringResults.Skip(1).Take(3).ToList();
            // 检测到所有值之间误差极小，假定这些值全部有效，计算其平均值
            if (difference < 5)
            {
                listResults.AddRange(MeasuringResults);
                string logContent = string.Format("检测到认为{0}次测量全部有效.", MeasuringResults.Count);
                Log.Write(logContent);
            }
            // 如果最大值最小值之差>10，那么认为偏大的值是无效的，去掉这些值
            // 偏大的或许检测的是到肩膀的距离，或者到座椅的距离，或者到肩背的距离
            // 如果测量的值与最大值相差10以上， 同时和最小值相差5以内，那么认为这次测量有效
            if (difference > 10)
            {
                for (int i = 0; i < MeasuringResults.Count; i++)
                {
                    if (max - MeasuringResults[i] > 10 && MeasuringResults[i] - min < 5)
                    {
                        listResults.Add(MeasuringResults[i]);
                    }
                }
                string logContent = string.Format("检测到有{0}次的身高值大约有效.", listResults.Count);
                Log.Write(logContent);
            }
            if (listResults.Count > 0)
            {
                int value = GetAverageValue(listResults);
                string logContent = string.Format("检测计算得到有效值为{0}.", value);
                Log.Write(logContent);
                return value;
            }
            else
                return 0;
        }

        private void SaveResult(int result)
        {
            if (measuringRangle.InRangle(result))
            {
                _outlierCount = 0;
                Log.Write("解析结果:" + result.ToString());
                MeasuringResults.Add(result);                
            }
            else
            {
                _outlierCount++;
                string strLog = string.Format("测量值{0}不在区间内[{1}, {2}], 放弃使用.", result, measuringRangle.Min, measuringRangle.Max);
                Log.Write(strLog);
                if (_outlierCount == 3)
                {
                    OnMeasuringAbnormal(result);
                }
            }
        }

        private void CheckMeasuringResults()
        {
            // 如果检测次数达到一个回合数，则从这个回合里面取出最可能的测量结果，
            // 处理完之后等待客户端通知开始下一个回合的测量
            if (MeasuringResults.Count >= TimesOfOneRounds)
            {
                _currentRoundsCount++;
                int value = GetRealMeasuringResultFromRounds();
                OnMeasuringUpdated(value);
                MeasuringResults.Clear();
                _sensor.Wait();
            }

            // 测量次数达到最大值了, 停止测量
            if (_currentRoundsCount >= MaxRoundsCount)
            {
                StopWork();
            }
        }
    }
}
